package com.auroramanito.uaa.service;

import com.auroramanito.uaa.config.Constants;
import com.auroramanito.uaa.domain.Auth;
import com.auroramanito.uaa.domain.User;
import com.auroramanito.uaa.repository.RoleRepo;
import com.auroramanito.uaa.repository.UserRepo;
import com.auroramanito.uaa.util.JwtUtil;
import com.auroramanito.uaa.util.TotpUtil;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.naming.AuthenticationException;
import java.util.Optional;
import java.util.Set;

@RequiredArgsConstructor
@Service
public class UserService {
    private final UserRepo userRepo;
    private final RoleRepo roleRepo;
    private final PasswordEncoder passwordEncoder;
    private final JwtUtil jwtUtil;
    private final TotpUtil totpUtil;

    @Transactional
    public User register(User user){
        User user1 = roleRepo.findOptionalByRoleName(Constants.ROLE_USER)
                .map(role -> {
                    val userToSave = user.withRoles(Set.of(role))
                            .withPassword(passwordEncoder.encode(user.getPassword()))
                            .withMfaKey(totpUtil.encodeKeyToString());
                    return userRepo.save(userToSave);
                })
                .orElseThrow();
        return user1;
    }

    public Auth login(String username,String password) {
        return userRepo.findOptionalByUsername(username)
                .filter(user -> passwordEncoder.matches(password,user.getPassword()))
                .map(user -> new Auth(jwtUtil.createAccessToken(user), jwtUtil.createRefreshToken(user)
                )).orElseThrow(()->new BadCredentialsException("用戶名或密碼錯誤。"));
    }


    /**
     * 判断用户名是否存在
     *
     * @param username 用户名
     * @return 存在与否
     * */
    public boolean isUsernameExisited(String username){
        return userRepo.countByUsername(username) > 0;
    }

    /**
     * 判断邮箱是否存在
     *
     * @param email 用户名
     * @return 存在与否
     * */
    public boolean isEmailExisted(String email){
        return userRepo.countByEmail(email) > 0;
    }

    /**
     * 判断手机号是否存在
     *
     * @param mobile 用户名
     * @return 存在与否
     * */
    public boolean isMobileExisted(String mobile){
        return userRepo.countByMobile(mobile) > 0;
    }

    /**
     * 根据用户名查询用户
     * @param username
     * @return
     * */
    public Optional<User> findOptionalByUsername(String username) {
        return userRepo.findOptionalByUsername(username);
    }

    /**
     * 根据用户电子邮件地址查找用户
     *
     * @param email 永湖电子邮件地址
     * @return 用户
     * */
    public Optional<User> findOptionalByEmail(String email){
        return userRepo.findOptionalByEmail(email);
    }

    /**
     * 根据用户名和密码查询用户并返回用户
     * @param username
     * @param password
     * @return
     */
    public Optional<User> findOptionalByUsernameAndPassword(String username, String password) {
        return userRepo.findOptionalByUsername(username).filter(user -> passwordEncoder.matches(password,user.getPassword()));
    }



    //进行无缝的密码升级
    public void updatePassword(User user, String newPassword) {
        if (passwordEncoder.upgradeEncoding(user.getPassword())) {
            userRepo.save(user.withPassword(passwordEncoder.encode(newPassword)));
        }
    }

    public Optional<String> createTotp(String key){
        return totpUtil.createTotp(key);
    }


    /**
     * 7.1 安全表达式配置。
     * */
    public boolean isValidUser(Authentication authentication, String username){
        return authentication.getName().equals(username);
    }
}
